package gameState;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Random;

import businessLogic.*;
import scala40rules.*;

public class Table implements Serializable{

	//table serial version id
	private static final long serialVersionUID= 2L;

	//TABLE SINGLETON
	private static final Table TABLE_INSTANCE = new Table();

	private CardSetList cardSetOnTheTable = new CardSetList();
	private ArrayList<CardSetList> playersCards = new ArrayList<CardSetList>();
	private ArrayList<Player> players = new ArrayList<Player>();

	//ha sceso 40?
	private ArrayList<Boolean> hasLaid = new ArrayList<Boolean>();

	// well = pozzo = carte scoperte
	// heel = tallone = carte coperte
	private CardSet well = new CardSet();
	private CardSet heel = new CardSet();

	private int version;

	private ArrayList<Integer> score= new ArrayList<Integer>();

	// turni mazziere
	private ArrayList<Player> dealerList = new ArrayList<Player>();

	private Player winner = null;
    private Player currentPlayer;

	private Table(){

	}

	public void resetTableForNewMatch(Ring ring){
		this.initializeCardSetOnTheTable();
		this.initializeHeelAndWell();
		this.initializePlayers(ring);
		this.updateDealerList();
		this.initializeHasLaid();
		this.initializeVersion();
		this.updateScore();
		this.initializePlayersCard();
	}


	public void resetTableForNewGame(Ring ring){
		this.initializeCardSetOnTheTable();
		this.initializeDealerList(ring);
		this.initializePlayers(ring);
		this.initializeHeelAndWell();
		this.initializeHasLaid();
		this.initializeScore();
		this.initializeVersion();
		this.initializePlayersCard();
	}

	public static synchronized Table getInstance(){
		return TABLE_INSTANCE;
	}

//	public static synchronized Table getInstance(){
//		//TODO just for testing....ho tolto il singleton;
//		return new Table();
//	}

//--------------------- METODI DI GESTIONE DEI MAZZI: COPERTO (heel) E SCOPERTO (weel) --------------------------//

	//pesca carta
	public Card getRandomCardFromHeel(){
		try{
			Random random = new Random();
			Card card = heel.removeCard(random.nextInt(heel.size()));
			return card;
		
		}catch(Exception e){
			System.out.println("- Exception- [Table || getRandomCardFromHeel()] Impossibile pescare; " +
					" this.heel.size()="+this.heel.size());
			return new Card();
		}
	}

	public boolean isHeelEmpty(){
		return (this.heel.size()>0);
	}

	//ripopola il mazzo delle carte coperte mettendoci le carte scoperte (eccetto l'ultima)
	public void popolateHeelFromWell(){
		try{
			Card lastWellCard = this.removeLastCardFromWell();
			this.heel.addCardSet(this.well);
			this.well = new CardSet();
			this.well.addCard(lastWellCard);
		
		}catch(Exception e){
			System.out.println("- Exception- [Table || popolateHeelFromWell()] Impossibile riempire il mazzo della carte coperte " +
					"con il mazzo delle carte scoperte; " +
					" this.heel.size()="+this.heel.size()+" this.well.size()="+this.well.size());
		}
	}

	//popola Heel per l'inizio di una nuova partita
	public void initializeHeelAndWell(){
		try{
			Deck deck = Deck.getInstance();
			this.heel = new CardSet();
			
			this.heel.addCardSet(deck.getDeck());
			this.well = new CardSet();
		
		}catch(Exception e){
			System.out.println("- Exception- [Table || initializeHeelAndWell()] Impossibile inizializzare i mazzi; " +
					" this.heel.size()="+this.heel.size()+" this.well.size()="+this.well.size());
		}
	}

	public CardSet getWell() {
		return well;
	}

	public void setWell(CardSet well) {
		this.well = well;
	}

	//scarta carta
	public void addToWell(Card card){
		try{
			this.well.addCard(card);
		}catch(Exception e){
			System.out.println("- Exception- [Table || addToWell()] Impossibile aggiungere carta al weel; " +
					" this.well.size()="+this.well.size());
		}
	}

	//pesca dal mazzo scoperto
	public Card removeLastCardFromWell(){
		try{
			return this.well.removeCard(this.well.size()-1);
		
		}catch(Exception e){
			System.out.println("- Exception- [Table || removeLastCardFromWell()] Impossibile rimuovere l'ultima carta dal mazzo scoperto; " +
					" this.well.size()="+this.well.size());
			return new Card();
		}
	}

	public Card getLastCardFromWell(){
		try{
			return this.well.getCard(this.well.size()-1);
		
		}catch(Exception e){
			System.out.println("- Exception- [Table || getRandomCardFromWell()] Impossibile ottenere la " +
					"carta in posizione "+(this.well.size()-1)+
					" this.well.size()="+this.well.size());
			return new Card();
		}
	}

	public CardSet getHeel() {
		return heel;
	}

	public void setHeel(CardSet heel) {
		this.heel = heel;
	}

	public void addHeel(Card card){
		this.heel.addCard(card);
	}

	public void addHeel(CardSet cards){
		this.heel.addCardSet(cards);
	}

//----------------------- METODI DI GESTIONE DEI PLAYER CHE HANNO APERTO IL GIOCO (=scalato40) --------------------------//

	public ArrayList<Boolean> getHasLaid() {
		return hasLaid;
	}


	public void setHasLaid(ArrayList<Boolean> hasLaid) {
		this.hasLaid = hasLaid;
	}

	public void initializeHasLaid(){
		try{
			int hasLaidSize = this.hasLaid.size();
			for(int i=0; i<hasLaidSize;i++){
				this.hasLaid.remove(0);
			}
			for(int i=0; i<this.players.size();i++){
				this.hasLaid.add(false);
			}
		}catch(Exception e){
			System.out.println("- Exception- [Table || initializeHasLaid()] Impossibile inizializzare hasLaid; " +
					" this.hasLaid.size()="+this.hasLaid.size());
		}
	}

	public boolean getHasLaid(int playerPosition){
		try{
			return this.hasLaid.get(playerPosition);
		
		}catch(Exception e){
			System.out.println("- Exception- [Table || getHasLaid()] Impossibile ottenere hasLaid " +
					" playerPosition="+playerPosition+
					" this.hasLaid.size()="+this.hasLaid.size());
			return false;
		}
	}

	public void setHasLaid(int playerPosition,boolean bool){
		try{
			this.hasLaid.set(playerPosition, bool);
		}catch(Exception e){
			System.out.println("- Exception- [Table || setHasLaid()] Impossibile settare hasLaid; " +
					" playerPosition="+playerPosition+
					" this.hasLaid.size()="+this.hasLaid.size());
		}
	}

	public void removeHasLaid(int playerPosition){
		try{
			this.hasLaid.remove(playerPosition);
		}catch(Exception e){
			System.out.println("- Exception- [Table || removeHasLaid()] Impossibile rimuovere da hasLaid; " +
					" playerPosition="+playerPosition+
					" this.hasLaid.size()="+this.hasLaid.size());
		}
	}

	//----------------------- METODI DI GESTIONE DELLE PLAYERS CARDS --------------------------//

	public ArrayList<CardSetList> getPlayersCards() {
		return playersCards;
	}

	
	//inizializzazione del vettore a tre dimensioni PlayerCard
	public void initializePlayersCard(){
		this.playersCards = new ArrayList<CardSetList>();
	
		if( this.playersCards.size()!=0)
			System.out.println("-Error- [Table || initializePlayersCard()] playersCard non inizializzato");
	}

	public void addPlayerCards(CardSetList csl){
		System.out.println("[Table || addPlayerCards()] sto per aggiungere un csl di grandezza="+csl.getCardSet(0).size());
		this.playersCards.add(csl);
		//System.out.println("[Table || addPlayerCards()] Aggiunte le carte del giocatore:"+this.players.get(this.playersCards.size()-1).getName());
	}

	//aggiunge un cardset al mazzo del giocatore senza specificare il livello
	public void addToPlayersCards(int playerPosition,CardSet cards){
		try{
			this.playersCards.get(playerPosition).addCardSet(cards);
		}catch(Exception e){
			System.out.println("- Exception- [Table || addToPlayersCards()] Impossibile aggiungere a playersCards; " +
					" playerPosition="+playerPosition+
					" this.playersCards.size()="+this.playersCards.size());
		}
	}

	//aggungere un cardSet al mazzo del giocatore specificandone il livello
	public void addToPlayersCards(int playerPosition, int level, CardSet cardSet){
		try{
			if(level>this.playersCards.get(playerPosition).size()){
				this.playersCards.get(playerPosition).addCardSet(cardSet);
			}else{
				this.playersCards.get(playerPosition).getCardSet(level).addCardSet(cardSet);
			}
			
		}catch(Exception e){
			try{
				System.out.println("- Exception- [Table || addToPlayersCards()] Impossibile aggiungere il cardSet nella " +
					" playerPosition="+playerPosition+" al livello="+level+
					" this.playersCards.size()="+this.playersCards.size()+"; ");
				this.playersCards.get(playerPosition).size();
			}catch(Exception exc){
				System.out.println("- Exception- [Table || addToPlayersCards()] Impossibile aggiungere il cardSet; " +
						" level="+level+
						"this.playersCards.get(playerPosition).size"+this.playersCards.get(playerPosition).size());
			}
		}
	}

	//aggiungere una carta al mazzo del giocatore in un particolare livello specificato in input
	public void addToPlayersCards(int playerPosition,int level,Card card){
		try{
			if(this.playersCards.get(playerPosition).size()== level){
				CardSet cardSet = new CardSet();
				cardSet.addCard(card);
				this.playersCards.get(playerPosition).addCardSet(cardSet);
			}else{
				if(this.playersCards.get(playerPosition).size()> level)
					this.playersCards.get(playerPosition).getCardSet(level).addCard(card);
				else
					System.out.println("-Error- [Table || addToPlayersCards()] Impossibile aggiungere carta; Livello "+level+" inesistente");
			}
		}catch(Exception e){
			System.out.println("- Exception- [Table || addToPlayersCards()] Impossibile aggiungere la carta; " +
					"playerPosition="+playerPosition+
					"this.playersCards.size"+this.playersCards.size());
		}
	}

	//riunisce in un unico cardSet tutte le carte di un determinato player
	public CardSet getPlayerCardTotal(int playerPosition){
		CardSet result=new CardSet();
		try{
			for(int i=0;i<this.playersCards.get(playerPosition).size();i++){
				result.addCardSet(this.playersCards.get(playerPosition).getCardSet(i));
			}
				
			return result;
		}catch(Exception e){
			System.out.println("- Exception- [Table || getPlayerCardTotal()] Impossibile le carte del player in posizione " +
					playerPosition+
					"this.playersCards.size"+this.playersCards.size());
			return result;
		}
	}

	//restituisce il cardSetList di un giocatore
	public CardSetList getPlayerCardSetList(int playerPosition){
		try{
			return this.playersCards.get(playerPosition);
		}catch(Exception e){
			System.out.println("-Exception- [Table || getPlayerCardSetList()] Impossibile ottenere le carte del player" +
					"in posizione "+playerPosition+" this.playersCards.size()"+this.playersCards.size());
			return new CardSetList();
		}
	}

	//restituisce un cardSet di un giocatore in un determinato livello
	//NB: carte in mano     --> sono nel livello 0
	//    carte nel baloon  --> dal livello 1 in poi
	//TODO errore.....mi da nullPointerExcepition
	public CardSet getPlayerCardSet(int playerPosition,int level){
		try{
			return this.playersCards.get(playerPosition).getCardSet(level);
			
		}catch(Exception e){
			System.out.println(" -[Exception]- [Table || getPlayerCardSet()] Impossibile ottenere le carte del player " +
					"in posizione "+playerPosition+", this.playersCards.size()="+this.playersCards.size());
				if(playerPosition<this.playersCards.size()){				
					System.out.println("                                              Livello " +level+
							", this.playersCards.get(playerPosition).size()="+this.playersCards.get(playerPosition).size());
				}
			return new CardSet();
		}
	}

	//rimuove, in riferimento ad un giocatore, una specifica carta di un determinato livello
	public Card removePlayerCard(int playerPosition,int level,int positionCard){
		try{
				return this.playersCards.get(playerPosition).getCardSet(level).removeCard(positionCard);
			
		}catch(Exception e){
			System.out.println(" -[Exception]- [Table || removePlayerCard()] Impossibile rimuovere la carta per outOfBound: \n"+
							   "                                              playerPosition="+playerPosition+" this.playersCards.size()="+this.playersCards.size());
			if(this.playersCards.size()>playerPosition){
				System.out.println("                                              level="+level+" this.playersCards.get(playerPosition).size()="+this.playersCards.get(playerPosition).size());
				if(this.playersCards.get(playerPosition).size()>level){
					System.out.println("                                              positionCard="+positionCard+" this.playersCards.get(playerPosition).getCardSet(level).size()="+this.playersCards.get(playerPosition).getCardSet(level).size());
				}
			}
			return new Card();
		}
	}

//------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------	
//------------------------------------------------------------------------------------------	
	//TODO da qui in poi mettere try catch
	
	
	
	//rimuove tutte le carte in un determinato livello
	public CardSet removePlayerCard(int playerPosition, int level){
		try{
			System.out.println("  [Table || removePlayerCard()] PRIMA this.playersCard.get(PlayerPosition).size()"+this.playersCards.get(playerPosition).size());
			CardSet cs = this.playersCards.get(playerPosition).removeCardSetFromList(level);
			System.out.println("  [Table || removePlayerCard()] DOPO this.playersCard.get(PlayerPosition).size()"+this.playersCards.get(playerPosition).size());
			
			return cs;
			
		}catch(Exception e){
			System.out.println(" -[Exception]- [Table || removePlayerCard()] Impossibile rimuovere le carte per outOfBound: \n"+
							   "                                              playerPosition="+playerPosition+" this.playersCards.size()="+this.playersCards.size());
			if(this.playersCards.size()>playerPosition){
				System.out.println("                                              level="+level+" this.playersCards.get(playerPosition).size()="+this.playersCards.get(playerPosition).size());
			}
			return new CardSet();
		}
	}

	public CardSet removeAllPlayerCard(int playerPosition){
		CardSet cs = new CardSet();
		if(this.playersCards.size()>playerPosition){
			System.out.println("[Table || removeAllPlayerCard()] sto per rimuovere tutte le carte del Player in posizione: "+playerPosition);
			cs.addCardSet(this.playersCards.get(playerPosition).removeAllCardSetNotListed());
			
			System.out.println("[Table || removeAllPlayerCard()] this.playersCards.get(PlayerPosition).size()= "+this.playersCards.get(playerPosition).size());
			if(this.playersCards.get(playerPosition).size()>0)
				System.out.println("[Table || removeAllPlayerCard()] this.playersCards.get(playerPosition).getCardSet(0).size()" + this.playersCards.get(playerPosition).getCardSet(0).size());
			else{
				System.out.println("[Table || removeAllPlayerCard()] IN TEORIA HO SVUOTATO LE CARTE DEL GIOCATORE");
			}
			this.playersCards.remove(playerPosition);
		}
		return cs;
	}

	public void setPlayersCards(ArrayList<CardSetList> csl){
		this.playersCards=csl;
	}

	//inserisco un nuovo CardSetList nella posizione del player in input
	public void setPlayersCards(int playerPosition, CardSetList pc) {
		if(playerPosition < this.playersCards.size()){
				this.playersCards.set(playerPosition, pc);
		}
	}

	//----------------------- METODI DI GESTIONE DELL'ARRAYLIST PLAYERS --------------------------//

	public ArrayList<Player> getPlayers() {
		return players;
	}

	public void setPlayers(ArrayList<Player> players) {
		this.players = players;
	}

	public void initializePlayers(Ring ring){
		int playerSize = this.players.size();
		for(int i=0; i<playerSize; i++){
			this.players.remove(0);
		}
		for(int i=0; i<ring.size(); i++){
			this.players.add(ring.getPlayer(i));
		}
	}

	public Player removePlayer(int playerPosition){
		try{
			return this.players.remove(playerPosition);
		}catch(Exception e){
			System.out.println("-Exception- [Table || removePlayer()] index out of bound. playerPosition="+playerPosition+" players.size()="+this.players.size());
		}
		return new Player();
	}

	public int getPlayerPosition(Player p){
		for(int i=0;i<this.players.size();i++){
			if(p.equalPlayer(this.players.get(i))){
				return i;
			}
		}
		System.out.println("[Table || getPlayerPosition()] non trovata la posizione del Player--> return -1");
		return -1;
	}

	public void addPlayer(Player player){
		this.players.add(player);
	}

	public Player getPlayer(int i){
		return players.get(i);
	}

//	public void sincronizePlayer(ArrayList<Player> p){
//		this.setPlayers(p);
//	}

	//----------------------- METODI DI GESTIONE DELLA VERSIONE --------------------------//

	public void initializeVersion(){
		this.version= -1;
	}

	public void incrementVersion(){
		this.version++;
		System.out.println("[Table || incrementVersion()] versione incrementata a "+this.version);
	}

	public int getVersion() {
		return version;
	}

	public void setVersion(int version) {
		this.version = version;
	}

	//----------------------- METODI DI GESTIONE DELLO SCORE --------------------------//

	public void initializeScore(){
		this.score = new ArrayList<Integer>();
		for(int i=0; i<this.players.size();i++){
			this.score.add(0);
		}
	}

	public void updateScore(){
		//TODO chiedere se si aggiunge 100...non sono sicura!!
		int maxScore = 100;

		System.out.println("[ Table || updateScore()] PLAYER SIZE " +this.players.size());
		for(int i=0; i<this.players.size();i++){
			int playersPosition= this.getPlayerPosition(this.players.get(i));

			CardSet totalCards= this.getPlayerCardTotal(playersPosition);
			if(totalCards.size()==13){
				System.out.println("[ Table || updateScore()] sto per aggiungere 100 al player"+ this.players.get(playersPosition).getName());
				this.addGameScore(playersPosition, maxScore);
			}else{
				if(totalCards.size()>0){
					int score = totalCards.sumNotValidCardsetValue();
					System.out.println("[ Table || updateScore()] sto per aggiungere "+score+" al player"+ this.players.get(playersPosition).getName());
					this.addGameScore(playersPosition, score);
				}
			}
		}
	}

	public ArrayList<Integer> getScore() {
		return score;
	}

	public Integer getScore(int playerPosition){
		return score.get(playerPosition);
	}

	public void setScore(ArrayList<Integer> score) {
		this.score = score;
	}

	public void addGameScore(int playerPosition,int score){
		int totalScore= this.score.get(playerPosition) + score;
		System.out.println("-------------------------------- new score"+totalScore);
		this.score.set(playerPosition, totalScore);
	}

	public void removeScore(int playerPosition){
		System.out.println("[Table || removeScore()] sto rimuovendo il punteggio del giocatore in posizione "+playerPosition);
		if(this.score.size()>playerPosition){
			this.score.remove(playerPosition);
		}else{
			System.out.println("-Error - [Table || removeScore()] rimozione non riuscita");
		}
	}

	//----------------------- METODI DI GESTIONE DELLA DEALERLIST --------------------------//

	public void initializeDealerList(Ring ring){
		this.dealerList.clear();
		for(int i=0; i<ring.size(); i++){
			this.dealerList.add(ring.getPlayer(i));
		}
	}

	public Player getFirstDealer(){
		return this.dealerList.get(0);
	}

	public Player getDealerList(int index){
		return this.dealerList.get(index);
	}

	public void updateDealerList(){
		System.out.println("[Table || updateDealerList()] stampa di controllo prima di fare update");
		for(int i=0; i<this.dealerList.size(); i++){
			System.out.println("        Turno "+ i +" Dealer= "+this.dealerList.get(i).getName());
		}
		System.out.println("                            - end Players -");
		Player last = this.dealerList.remove(0);
		this.dealerList.add(last);
		
		//for testing
		System.out.println("[Table || updateDealerList()] stampa di controllo");
		for(int i=0; i<this.dealerList.size(); i++){
			System.out.println("        Turno "+ i +" Dealer= "+this.dealerList.get(i).getName());
		}
		System.out.println("                            - end Players -");
	}

	public ArrayList<Player> getDealerList() {
		return dealerList;
	}

	public void setDealerList(ArrayList<Player> dealerList) {
		this.dealerList = dealerList;
	}

	public void addDealer(Player dealer){
		this.dealerList.add(dealer);
	}

	public int getPlayerDealerListPosition(Player p){
		for(int i=0;i<this.dealerList.size();i++){
			if(p.equalPlayer(this.dealerList.get(i))){
				return i;
			}
		}
		System.out.println("[Table || getPlayerDealerListPosition()] non trovata la posizione del Player nella dealerList--> return -1");
		return -1;
	}

	public void removeDealerList(int playerDealerListPosition){
		this.dealerList.remove(playerDealerListPosition);
	}

	//----------------------- METODI DI GESTIONE DEL PLAYER WINNER --------------------------//

	public Player getWinner() {
		return winner;
	}

	public void setWinner(Player winner) {
		this.winner = winner;
		if( this.winner != null){
			System.out.println("[Table || setWinner()] Il vincitore e': "+this.winner.getName());
		}
	}

	//----------------------- METODI DI GESTIONE DELLE CARTE SUL TAVOLO --------------------------//

	public void initializeCardSetOnTheTable(){
		int levelSize = this.cardSetOnTheTable.size();
		for(int level=0; level<levelSize;level++){
			this.cardSetOnTheTable.removeCardSetFromList(0);
		}
	}

	public CardSetList getCardSetOnTheTable() {
		return cardSetOnTheTable;
	}

	public void setCardSetOnTheTable(int level,CardSet cardSet){
		this.cardSetOnTheTable.setCardSetList(level, cardSet);
	}

	public CardSet getCardSetOnTheTable(int level){
		return this.cardSetOnTheTable.getCardSet(level);
	}

	public CardSet removeCardSetOnTheTable(int level){
		return this.cardSetOnTheTable.removeCardSetFromList(level);
	}

	public void addCardSetOnTheTable(CardSet cardSet){
		this.cardSetOnTheTable.addCardSet(cardSet);
	}

	public void addCardSetListOnTheTable(CardSetList cardSetList){
		this.cardSetOnTheTable.addCardSetList(cardSetList);
	}

	public boolean equalsCardSetOnTheTable(CardSetList csl){
		return this.cardSetOnTheTable.equals(csl);
	}

	public void setCardSetOnTheTable(CardSetList csl){
		this.cardSetOnTheTable=csl;
	}

	//----------------------- GESTIONE DEI CRASH --------------------------------------//

	public void updateTableWithAlivePlayers(Ring ring){
		System.out.println("[Table || updateTableWithPlayerAlive] nel precedente turno si" +
							"sono ritirati dei giocatori" +
							" percio' il tavolo deve essere aggiornato.\n" +
							" Il tavolo che ho ora e' cosi': ");
		this.stampaTuttoTavolo();
		ArrayList<Integer> playerDeadPosition = new ArrayList<Integer>();
		ArrayList<Player> playerDead = new ArrayList<Player>();
		for(int i=0; i<this.players.size(); i++){
			Player p = this.players.get(i);
			if(!ring.existPlayerInTheRing(p)){
				System.out.println("[Table || updateTableWithAlivePlayers()] Morto Player "+p.getName());
				playerDead.add(p);
				playerDeadPosition.add(this.getPlayerPosition(p));
			}
		}
		//aggiornamento di tutte le altre strutture dati in base ai giocatori che
		//non partecipano piu' al gioco
		for(int i=0; i<playerDead.size();i++){
			int playerPosition = this.getPlayerPosition(playerDead.get(i));
			System.out.println("[Table || updateTableWithAlivePlayers] adesso cancelliamo dal TABLE questo qiocatore: "+playerDead.get(i).getName());
			System.out.println("[Table || updateTableWithAlivePlayers] sto rimuovendo il player in posizione "+playerPosition);
			this.removePlayer(playerPosition);
			this.removeHasLaid(playerPosition);
			CardSet cards = this.removeAllPlayerCard(playerPosition);
			System.out.println("[Table || updateTableWithAlivePlayers] gli ho rimosso le carte: ");
			cards.printCardset();
			System.out.println("[Table || updateTableWithAlivePlayers] infatti ora PlayersCars.size() "+this.playersCards.size());
			this.addHeel(cards);
			System.out.println("[Table || updateTableWithAlivePlayers] che ho messo nel mazzo ");
			this.removeScore(playerPosition);
			int playerDealerListPosition = this.getPlayerDealerListPosition(playerDead.get(i));
            System.out.println("[Table || updateTableWithAlivePlayers] dimensione dealerList prima del remove: "+this.dealerList.size());
            System.out.println("[Table || updateTableWithAlivePlayers] playerDealerListPosition:"+playerDealerListPosition);
			this.removeDealerList(playerDealerListPosition);
            System.out.println("[Table || updateTableWithAlivePlayers] dimensione dealerList dopo il remove: "+this.dealerList.size());
        }
		System.out.println("---------------------------------------------------------------------");
		System.out.println("[Table || updateTableWithPlayerAlive] TAVOLO AGGIORNATO \n" +
				" Il tavolo che ho ora e' cosi': ");
		this.stampaTuttoTavolo();
		System.out.println("---------------------------------------------------------------------");
    }

	//----------------------- SINCRONIZZAZIONE DEL TAVOLO ----------------------------------//
	
	public void tableSincronize(Table t, int ringPlayerPosition,int tablePlayerPosition, boolean missingPlayers){
		System.out.println("[Table || tableSincronize()]");
		this.setCardSetOnTheTable(t.getCardSetOnTheTable());
		
		this.setHeel(t.getHeel());
		this.setWell(t.getWell());

		// versione == 1  --> sono appena state distribuite le carte
		// versione == 0  --> e' stato settato il mazziere
		// versione == -1 --> il tavolo e' stato appena inizializzato
		if(( t.version == 1 ) || (t.version == -1) || (t.version==0)){
			this.setPlayersCards(t.getPlayersCards());
		}else{
			//avendo gia' sincronizzato il vettore Players, e avendo playerPosition dal ring...
			//prima di tutto sposto in table il mio attuale stato delle carte nella mia corretta posizione [ringPlayerPosition]
			//(per mantenere eventuale spostamento di carte e aggiunte al baloon)
			//poi aggiorno tutte le carte dei players eccetto le mie
			CardSetList csl =  this.getPlayerCardSetList(tablePlayerPosition);
			
			if(missingPlayers){
				for(int i = 0; i<this.players.size(); i++){
					boolean isThere = false;
					for(int j = 0; j<t.getPlayers().size(); j++){
						if(this.players.get(i).equalPlayer(t.getPlayer(j))){
							isThere = true;
						}
					}
					if(!isThere){
						this.playersCards.remove(i);
						this.players.remove(i);
						i--;
					}
				}
			}
			this.setPlayersCards(ringPlayerPosition, csl);
			for(int i=0; i<this.playersCards.size();i++){
				if( (i!= ringPlayerPosition) && (i<t.getPlayersCards().size()) ){
					this.setPlayersCards(i, t.getPlayerCardSetList(i));
				}
			}
			
		}

		this.setPlayers(t.getPlayers());
		this.setDealerList(t.getDealerList());
		this.setScore(t.getScore());
		this.setVersion(t.getVersion());
		this.setHasLaid(t.getHasLaid());
		this.setWinner(t.getWinner());
		System.out.println("[Table || tableSincronize()] sto per sincronizzare il currentPlayer a "+t.getCurrentPlayer().getName());
		this.setCurrentPlayer(t.getCurrentPlayer());
	}





	// -------------------------------- METODI PER IL TESTING ------------------------ //

	public void forTestSetPlayerCard(int playerPosition, int level, CardSet cardSet){
		this.playersCards.get(playerPosition).removeCardSetFromList(level);
		this.playersCards.get(playerPosition).setCardSetList(level, cardSet);
	}

	public void stampaCartePlayer(int playerPosition){
		CardSet carteInMano = this.playersCards.get(playerPosition).getCardSet(0);
		int sizeCarteInMano = carteInMano.size();
		System.out.println(" Carte in mano al Player "+ this.players.get(playerPosition).getName()+":");
		for(int i=0; i<sizeCarteInMano; i++){
			System.out.println(" nÔøΩ"+ i+": "+ carteInMano.getCard(i).getSymbol()+" "+carteInMano.getCard(i).getSuit());
		}
		int sizeBaloon= this.playersCards.get(playerPosition).size();

		if( sizeBaloon>1){
			System.out.println("Carte nel Baloon");
			for(int i=1; i<sizeBaloon; i++){
				System.out.println("CardSet n."+(i-1)+":");
				for(int j=0; j<this.getPlayerCardSet(playerPosition, i).size(); j++){
					System.out.println("  "+this.getPlayerCardSet(playerPosition, i).getCard(j).getSymbol()+" "+this.getPlayerCardSet(playerPosition, i).getCard(j).getSuit());
				}
			}
		}
	}

	public void stampaCarteSulTavolo(){
		if( this.getCardSetOnTheTable().size()>0 ){
			System.out.println(" * Carte sul tavolo * ");
			for(int i=0; i<this.cardSetOnTheTable.size(); i++){
				System.out.println("CardSet n."+i+":");
				for(int j=0; j<this.cardSetOnTheTable.getCardSet(i).size(); j++){
					System.out.println("  "+this.cardSetOnTheTable.getCardSet(i).getCard(j).getSymbol()+" "+this.cardSetOnTheTable.getCardSet(i).getCard(j).getSuit());
				}
			}
		}
	}

	public void stamaScore(){
		for(int i=0; i<this.score.size(); i++){
			System.out.println(" TotalScore Player:"+this.getPlayer(i).getName()+" score:"+this.score.get(i));
		}
	}

	public void stampaTuttoTavolo(){
		int numTotalDeckCards = 0;
		numTotalDeckCards += this.well.size();
		numTotalDeckCards += this.heel.size();
		
		System.out.println(" ------------ TAVOLO ----------------------------");
		System.out.println(" ------- Version n "+this.version+" ----------");
		System.out.println("**** currentPlayer: "+this.currentPlayer.getName());
		if(this.winner != null)
			System.out.print("\n Winner:" + this.winner.getName() +"\n");
		System.out.println("\n **** Players gameState: ****\n\n");
		for(int i=0; i<this.players.size(); i++){
			numTotalDeckCards += this.getPlayerCardTotal(this.getPlayerPosition(this.players.get(i))).size();
			System.out.println(" Player "+ this.players.get(i).getName());
			System.out.println("  + Score:"+ this.score.get(this.getPlayerPosition(this.players.get(i))));
			System.out.println("  + hasLaid:"+this.hasLaid.get(i));
			System.out.println("  + Carte in mano:"+this.getPlayerCardTotal(this.getPlayerPosition(this.players.get(i))).size());
			if( this.getPlayerCardTotal(this.getPlayerPosition(this.players.get(i))).size()>0)
				this.stampaCartePlayer(i);
			System.out.println("****        *****       ****");
		}
		
		for(int i=0; i<this.getCardSetOnTheTable().size();i++){
			numTotalDeckCards += this.getCardSetOnTheTable(i).size();
		}
		
		if(numTotalDeckCards!=108){
			System.out.println("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
			System.out.println("@@  LE CARTE NON SONO 108   @@");
			System.out.println("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
			System.out.println(" le carte sono " +numTotalDeckCards);
		}
		
		this.stampaCarteSulTavolo();
	}

	public void setCurrentPlayer(Player p) {
            this.currentPlayer = p;
	}

	public Player getCurrentPlayer(){
            return this.currentPlayer;
	}


    public Player getSuccessivePlayer(Player p){
    	try{
	        for(int i=0; i<players.size(); i++){
	            if(players.get(i).equalPlayer(p)){
	                return players.get(((i+1) % players.size()));
	            }
	        }

    	}catch(Exception e){
			System.out.println("- Excepition- [Table || getSuccessivePlayer()] Impossibile ottenere " +
					"il Player successivo del Player "+p.getName()+
					" this.players.size()="+this.players.size());
			return new Player();
		}

        System.out.println("[Table || getSuccessivePlayer()] finito ciclo for e NON trovato currentPlayer");
        return null;
    }

}
